package de.tudarmstadt.fgbs.cafe.core;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.naming.CommunicationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PropertyConfigurator;

import de.tudarmstadt.fgbs.cafe.exception.FlawedConfigurationException;
import de.tudarmstadt.fgbs.cafe.filesystem.AutoSaveInterface;
import de.tudarmstadt.fgbs.cafe.importexport.ExportCoreInterface;
import de.tudarmstadt.fgbs.cafe.importexport.ImportCoreInterface;
import de.tudarmstadt.fgbs.cafe.structure.Bahnhof;
import de.tudarmstadt.fgbs.cafe.structure.Fahrplan;
import de.tudarmstadt.fgbs.cafe.structure.PluginStruct;
import de.tudarmstadt.fgbs.cafe.structure.Zuglauf;
import de.tudarmstadt.fgbs.cafe.ui.UICoreInterface;

public class MicroCoreSystem implements UICoreInterface, ExportCoreInterface,
	ImportCoreInterface, AutoSaveInterface {

    private static volatile MicroCoreSystem instance;

    private SubsystemFactory subsystemFactory;

    private static Log LOG = LogFactory.getLog(MicroCoreSystem.class);

    /**
     * The Constructor load the subsystemFactory.
     */
    private MicroCoreSystem() {
	// BasicConfigurator replaced with PropertyConfigurator.
	String baseDir = System.getProperty("cafe.base.dir");
	if (baseDir == null || baseDir == "") {
	    if (LOG.isWarnEnabled())
		LOG.warn("# cafe.base.dir Path variable not set - defaulting to current directory "
			+ System.getProperty("user.dir") + " configs");
	    baseDir = System.getProperty("user.dir");
	}
	PropertyConfigurator.configure(baseDir + File.separatorChar
		+ "log4j.properties");
	this.subsystemFactory = SubsystemFactory.getInstance();
	if (LOG.isTraceEnabled())
	    LOG.trace("# new instance of MicroCoreSystem is created");
    }

    /**
     * Checks the user's authentication via password.
     * 
     * @param username
     *            The name of the user.
     * @param password
     *            The password of the user.
     * @return <code>true</code> if the user is authorized to use the system,
     *         otherwise <code>false</code>
     * @throws FlawedConfigurationException
     *             If the configuration is faulty and no instance of the
     *             required class can be created.
     * @throws CommunicationException
     *             If a remote service is used and an error occurred while
     *             communicate with this server.
     * @throws IOException
     *             If a required configuration can not be found.
     */
    @Override
    public boolean login(String username, String password)
	    throws FlawedConfigurationException, CommunicationException,
	    IOException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.login()");
	return subsystemFactory.getLoginSubsystem().login(username, password);
    }

    /**
     * Gets the only instance existing of {@link MicroCoreSystem}. The
     * synchronized method {@link #getSynchronizedInstance()} is used to
     * delegate the creation of a new instance, because of thread safety.
     * 
     * @return An instance of {@link MicroCoreSystem}.
     */
    public static MicroCoreSystem getInstance() {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.getInstance()");
	if (instance == null) {
	    instance = getSynchronizedInstance();
	}
	return instance;
    }

    /**
     * Gets the only instance existing of {@link MicroCoreSystem}. This method
     * is synchronized, because of the needed thread safety.
     * 
     * @return An instance of {@link MicroCoreSystem}.
     */
    private static synchronized MicroCoreSystem getSynchronizedInstance() {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.getSynchronizedInstance()");
	if (instance == null) {
	    instance = new MicroCoreSystem();
	}
	return instance;
    }

    @Override
    public List<PluginStruct> getPlugins() throws FlawedConfigurationException {
	// TODO Auto-generated method stub
	return null;
    }

    @Override
    public int createFPL(String name) throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.createFPL(" + name + ")");
	return subsystemFactory.getSystemDataMangerSubsystem().createFPL(name);
    }

    @Override
    public boolean addZL(int fplID, Zuglauf zl)
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.addZL(" + fplID + ", "
		    + zl.getNumber() + ")");
	return subsystemFactory.getSystemDataMangerSubsystem().addZL(fplID, zl);
    }

    @Override
    public Fahrplan importFPL(String location)
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.importFPL(" + location + ")");
	Fahrplan fpl = subsystemFactory.getImportSubsystem(this).importFPL(
		location);
	subsystemFactory.getSystemDataMangerSubsystem().addFPL(fpl);
	return fpl;
    }

    @Override
    public boolean exportFPL(int fplID, String name, String location)
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.exportFPL(" + fplID + ", " + name
		    + ", " + location + ")");
	return subsystemFactory.getExportSubsystem(this).exportFPL(fplID, name,
		location);
    }

    @Override
    public Map<Integer, Fahrplan> retrieveAllFPL()
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.retrieveAllFPL()");

	return subsystemFactory.getSystemDataMangerSubsystem().retrieveAllFPL();
    }

    @Override
    public Fahrplan retrieveFPL(int id) throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.retrieveFPL(" + id + ")");

	return subsystemFactory.getSystemDataMangerSubsystem().retrieveFPL(id);
    }

    @Override
    public Zuglauf retrieveZL(int fplID, String zlNr)
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.retrieveZL(" + fplID + ", " + zlNr
		    + ")");

	return subsystemFactory.getSystemDataMangerSubsystem().retrieveZL(
		fplID, zlNr);
    }

    @Override
    public Map<String, Bahnhof> retrieveBahnhofMap()
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.retrieveBahnhofMap()");
	return subsystemFactory.getDataRetrieverSubsystem()
		.retrieveBahnhofMap();
    }

    @Override
    public Fahrplan retrieveFPL(String name)
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.retrieveFPL(" + name + ")");

	return subsystemFactory.getSystemDataMangerSubsystem()
		.retrieveFPL(name);
    }

    @Override
    public int getFPLID(Fahrplan fpl) throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.getFPLID(" + fpl.getName() + ")");
	return subsystemFactory.getSystemDataMangerSubsystem().getFPLID(fpl);
    }

    @Override
    public boolean exportFPL(Fahrplan fpl, String name, String absolutePath)
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.exportFPL(" + fpl.getName() + ", "
		    + name + ", " + absolutePath + ")");
	return subsystemFactory.getExportSubsystem(this).exportFPL(fpl, name,
		absolutePath);

    }

    @Override
    public void saveCurrentWork(Fahrplan fpl)
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.saveCurrentWork(" + fpl.getName()
		    + ")");
	subsystemFactory.getAutoSaveSubsystem().saveCurrentWork(fpl);
    }

    @Override
    public Fahrplan restorePreviousWork(int undoSteps)
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.restorePreviousWork(" + undoSteps
		    + ")");
	return subsystemFactory.getAutoSaveSubsystem().restorePreviousWork(
		undoSteps);
    }

    @Override
    public void removeFPL(String name) throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.removeFPL(" + name + ")");
	subsystemFactory.getSystemDataMangerSubsystem().removeFPL(name);
    }

    @Override
    public Fahrplan mergeFPLs(String[] names)
	    throws FlawedConfigurationException {
	if (LOG.isTraceEnabled())
	    LOG.trace("# MicroCoreSystem.mergeFPLs(" + names.toString() + ")");
	return subsystemFactory.getSystemDataMangerSubsystem().mergeFPLs(names);
    }
}
